# coding=utf-8

import os
import Image
import web
from web.utils import storage

import utils
import settings
from db import connection
from administrators import auth_login

db = connection.default
render = settings.RENDER

# Components
class MediaFile(object):
    """
    表示一个媒体文件对象
    """
    # 保存成功
    SUCCESS = 'OK'
    # 文件格式无效
    FILE_IS_INVALID = '文件不是有效的图像文件'
    # 文件类型错误
    FILE_TYPE_ERROR = '文件类型错误（仅限：JPG/GIF/PNG）'
    def __init__(self, http_file):
        self._raw_file = http_file
        # 文件名
        self.filename = None
        # 文件扩展名
        self.extension = None
        # 媒体文件类型
        self.mimetype = None
        # 文件尺寸
        self.size = 0
        # 文件宽度及高度
        self.width = 0
        self.height = 0
        # 上传的图片文件实例
        self._image = None
        # 文件id
        self.id = 0

    def save(self):
        """
        保存媒体文件
        """
        # 提取文件信息
        status = self._resolve_file_info()
        if status != MediaFile.SUCCESS:
            return status

        # 文件信息提取成功
        # 分配文件id
        self.id = self._ass_file_id()
        # 根据分配的id，生成保存路径
        path = self._create_path()
        # 保存源文件
        self._save_source(path)
        # 生成1号缩略图
        self._save_thumbnail_1(path)
        # 生成正方形缩略图
        self._save_thumbnail_square(path)

        # 保存完成，更新数据库
        self._update_db()

        return MediaFile.SUCCESS

    def _save_thumbnail_square(self, path):
        """
        保存正方形缩略图
        
        @path as str, 保存文件的路径

        file postfix:
        2 - 较大的正方形缩略图
        3 - 较小的正方形缩略图
        """
        from Image import EXTENT

        if self.width > self.height:
            # 当图片宽度大于高度时
            offset = int(self.width - self.height) / 2
            img = self._image.transform((self.height, self.height)
                    , EXTENT
                    , (offset, 0, int(self.width - offset), self.height))
        else:
            # 当图片高度大于宽度时
            offset = int(self.height - self.width) / 2
            img = self._image.transform((self.width, self.width)
                    , EXTENT 
                    , (0, offset, self.width, (self.height - offset)))

        # 先存较大的图
        img.thumbnail((500, 500), Image.ANTIALIAS)
        img.save('%s-2%s' % (path, settings.MEDIA_THUMBNAIL_EXTENSION))
        # 再存较小的图
        img.thumbnail((180, 180), Image.ANTIALIAS)
        img.save('%s-3%s' % (path, settings.MEDIA_THUMBNAIL_EXTENSION))


    def _save_thumbnail_1(self, path):
        """
        保存1号缩略图
        1号缩略图是等比例图
        """
        # 生成缩略图
        # 缩略图宽度
        tx = settings.MEDIA_THUMBNAIL_WIDTH
        # 计算缩略图高度
        ty = self.height * tx / self.width
        # 生成缩略图对象
        thumbnail = self._image.resize((tx, ty), Image.ANTIALIAS)
        # 保存所省略图
        thumbnail.save('%s-1%s' % (path, settings.MEDIA_THUMBNAIL_EXTENSION))

    def _save_source(self, path):
        """
        保存源文件
        """
        # 源文件保存路径
        filename = '%s%s' % (path, self.extension)
        # 保存源文件
        self._image.save(filename)
        # 获取文件大小
        self.size = os.path.getsize(filename)
        # 返回源文件路径，可能会有用
        return filename
        
    def _update_db(self):
        """
        将文件信息更新到数据库
        """
        return db.update('media'
                , title = self.filename
                , raw_name = self.filename
                , extension = self.extension
                , file_type = self.mimetype
                , size = self.size
                , width = self.width
                , height = self.height
                , where = 'id = $id'
                , vars = { 'id': self.id })

    def _create_path(self):
        """
        生成并创建媒体文件的保存路径
        """
        # 获取物理路径
        ret = settings.MEDIA_PATH
        # 创建目录
        self._create_dir(ret)
        # 获取媒体文件的关键部分路径
        path = utils.create_media_part(self.id)
        # 切分路径，以便逐级创建
        parts = path.split('/')
        # 逐级创建目录
        for i in range(len(parts) - 1):
            ret = os.path.join(ret, parts[i])
            self._create_dir(ret)

        return os.path.join(settings.MEDIA_PATH, path)
    
    def _create_dir(self, path):
        if not os.path.isdir(path):
            os.mkdir(path)

    def _resolve_file_info(self):
        """
        提取图像文件信息
        """
        # 确保媒体文件是一个图像文件
        self._image = utils.is_image(self._raw_file.file)
        if not self._image:
            return MediaFile.FILE_IS_INVALID

        # 获取文件名
        (filepath, self.filename) = os.path.split(self._raw_file.filename)
        # 获取扩展名
        self.extension = os.path.splitext(self._raw_file.filename)[1]
        try:
            # 获取文件类型
            self.mimetype = {
                    '.jpg': 'image/jpeg',
                    '.jpeg': 'image/jpeg',
                    '.gif': 'image/gif',
                    '.png': 'image/png',
                    }[self.extension.lower()]
        except:
            return MediaFile.FILE_TYPE_ERROR

        # 获取文件尺寸
        (self.width, self.height) = self._image.size
        
        # 有效提取
        return MediaFile.SUCCESS


    def _ass_file_id(self):
        """
        获取并分配一个文件id
        """
        return db.insert('media', date_created = web.SQLLiteral('NOW()'))

# Views
class MediaRebuild(object):
    """
    重建媒体文件
    """
    @auth_login
    def GET(self):
        # 读取所有的媒体文件
        mds = load_all_medias()
        if mds:
            for md in mds:
                self.rebuild(md)
        
        return 'ALL OK!'

    def rebuild(self, md):
        """
        重建给定的媒体文件
        """
        path = self._create_path(md.id)
        # 源图
        image = utils.is_image('%s%s' % (path, md.extension))
        if image:
            self._save_thumbnail_square(image, path)

    def _create_path(self, id):
        """
        生成并创建媒体文件的保存路径
        """
        # 获取物理路径
        ret = settings.MEDIA_PATH
        # 创建目录
        self._create_dir(ret)
        # 获取媒体文件的关键部分路径
        path = utils.create_media_part(id)
        # 切分路径，以便逐级创建
        parts = path.split('/')
        # 逐级创建目录
        for i in range(len(parts) - 1):
            ret = os.path.join(ret, parts[i])
            self._create_dir(ret)

        return os.path.join(settings.MEDIA_PATH, path)

    def _create_dir(self, path):
        if not os.path.isdir(path):
            os.mkdir(path)

    def _save_thumbnail_square(self, image, path):
        """
        保存正方形缩略图
        
        @path as str, 保存文件的路径

        file postfix:
        2 - 较大的正方形缩略图
        3 - 较小的正方形缩略图
        """
        from Image import EXTENT

        (width, height) = image.size
        if width > height:
            # 当图片宽度大于高度时
            offset = int(width - height) / 2
            img = image.transform((height, height)
                    , EXTENT
                    , (offset, 0, int(width - offset), height))
        else:
            # 当图片高度大于宽度时
            offset = int(height - width) / 2
            img = image.transform((width, width)
                    , EXTENT 
                    , (0, offset, width, (height - offset)))

        # 先存较大的图
        img.thumbnail((500, 500), Image.ANTIALIAS)
        img.save('%s-2%s' % (path, settings.MEDIA_THUMBNAIL_EXTENSION))
        # 再存较小的图
        img.thumbnail((180, 180), Image.ANTIALIAS)
        img.save('%s-3%s' % (path, settings.MEDIA_THUMBNAIL_EXTENSION))


class MediaIndex(object):
    @auth_login
    def GET(self):
        pass

class MediaUpload(object):
    @auth_login
    def GET(self):
        ipt = web.input()
        return render.admin.media_upload(mce = ipt.get('mce', 'false')
                , thumb = ipt.get('thumb', '1'))

    @auth_login
    def POST(self):
        """
        保存对媒体文件标题的修改
        同时回调函数
        """
        # 选择的媒体文件id
        sid = 0
        stitle = '';
        # 获取用户提交信息
        ipt = web.input(title = [],
                        media_id = [],
                        save = 'False',
                        mce = 'false',
                        thumb = 1)
        if ipt.media_id:
            for i in range(len(ipt.media_id)):
                # 表示当前循环的媒体记录是否被选中
                selected = False
                id = utils.intval(ipt.media_id[i])
                if id > 0:
                    # 确定点击的哪一个选择按钮
                    if ipt.get('send[%s]' % id, 'False') != 'False':
                        sid = id
                        stitle = ipt.title[i]
                        # 点击的哪个插入按钮就保存哪个
                        # 表示被选中，那么在下面将会更新文本框的信息
                        selected = True
                    # 如果用户点击的“保存所有更改”按钮
                    # 则无论该媒体文件是否被选中，都进行保存
                    if selected or ipt.save != 'False':
                        # 标题是否填写，若未填写标题，则不更新
                        if ipt.title[i]:
                            update_media_item(id, ipt.title[i])

        if sid > 0:
            # 是否来自editor的请求
            if ipt.mce == 'false':
                # 若点击的“选择此图像”提交
                # 那么这里要回调上一层页面的回调函数
                return """<script type=\"text/javascript\">
                            var topWin = window.dialogArguments || opener || parent || top;
                            // selection media file
                            function selectedFile(id, thumbnail) {
                                if (topWin && topWin.media_selected) {
                                    topWin.media_selected(id, thumbnail);
                                } else {
                                    alert('Callback function is not defined.');
                                }
                            }
                            selectedFile(%s, '%s');
                        </script>
                """ % (sid, utils.get_media_url(sid, ipt.thumb))
            else:
                # 来自editor请求
                # 直接添加到编辑器中
                return """<script type="text/javascript">
                            var topWin = window.dialogArguments || opener || parent || top;
                            topWin.insert_to_editor(%(id)d,'%(title)s','%(url)s','%(thumbnail)s');
                        </script>
                """ % {'id' : sid, 
                        'url': utils.get_media_url(sid, 0),
                        'thumbnail': utils.get_media_url(sid, thumb),
                        'title': stitle}
        else:
            # 若点击的“保存所有更改”提交
            # 那么这里直接转向到媒体库
            raise web.seeother('/media/library?mce=%s&thumb=%s' % (ipt.mce,
                ipt.thumb))

class MediaLibrary(object):
    @auth_login
    def GET(self):
        # 每页记录数
        perpage = 10

        # 获取用户请求信息
        ipt = web.input()
        # 获取当前页码
        page = utils.intval(ipt.get('page', '0'))
        # 检索媒体的条件
        where = ''
        # 检索关键字
        q = ipt.get('q', False)
        if q:
            where = " AND title LIKE '%" + utils.sqlescape(q) + "%'"
        # 获取结果集
        result = load_media_items(where, page * perpage, perpage)
        # 创建分页器
        pager = utils.Pager(result.totals, pageindex = page
                        , first = False, last = False
                        , link = '<a class="page-numbers" href="?page=%(page)s">%(text)s</a>&nbsp;'
                        , current = '<span class="page-numbers current">%(text)s</span>&nbsp;'
                        , next = '<a class="next page-numbers" href="?page=%(page)s">%(text)s</a>'
                        , prev = '<a class="prev page-numbers" href="?page=%(page)s">%(text)s</a>&nbsp;'
                        , nexttext = '»'
                        , prevtext = '«')

        media_items = ''
        if result.totals > 0:
            for media in result.result:
                media_items += '<div id="media-item-%s" class="media-item child-of-0 preloaded">%s</div>' % (media.id, str(render.admin.media_upload_item(media, utils.get_media_url)))

        return render.admin.media_library(media_items
                , pager
                , ipt.get('mce', 'false')
                , ipt.get('thumb', 1))

class MediaAsyncUpload(object):
    @auth_login
    def POST(self):
        ipt = web.input(async_upload={});
        attachment_id = ipt.get('attachment_id', 0)
        if (attachment_id > 0):
            return self.load_media_item(attachment_id)

        # 创建媒体文件实例
        media = MediaFile(ipt.async_upload)
        # 保存
        status = media.save()
        if status == MediaFile.SUCCESS:
            # 上传成功
            # 返回媒体记录id
            return str(media.id)
        else:
            # 上传失败
            # 返回错误信息
            return '<div class="media-upload-error">%s</div>' % status

    def load_media_item(self, media_id):
        """
        读取指定媒体记录
        """
        item = load_media_item(media_id)
        if not item:
            return '<div class="media-upload-error">媒体文件不存在</div>'

        return render.admin.media_upload_item(item, utils.get_media_url)

class MediaDelete(object):
    """
    删除媒体
    """
    @auth_login
    def GET(self, id):
        id = utils.intval(id)
        if id > 0:
            delete_media_item(id)

        raise web.seeother('/media/library')

# Models
def load_all_medias():
    """
    读取所有的媒体文件
    """
    return db.select('media')

def load_media_items(where, offset, length):
    """
    获取所有媒体记录项
    """
    # 获取结果集
    sets = db.query("""
                SELECT SQL_CALC_FOUND_ROWS * 
                FROM media
                WHERE is_deleted = 0 %s
                ORDER BY id DESC
                LIMIT $offset, $length;
            """ % where
            , vars = locals())
    # 获取总记录数
    totals = db.query('SELECT FOUND_ROWS() AS totals;')
    return storage(result = sets
            , totals = int(totals[0].totals))
    

def load_media_item(id):
    """
    读取指定媒体记录
    """
    sets = db.select('media', where = 'id = $id', vars = locals())
    if sets:
        return sets[0]
    return None

def update_media_item(id, title):
    """
    更新媒体项
    """
    return db.update('media'
            , title = title
            , where = 'id = $id'
            , vars = locals())

def delete_media_item(id):
    """
    删除给定媒体项（逻辑删除）
    """
    return db.update('media'
            , is_deleted = 1
            , where = 'id = $id'
            , vars = locals())
